#pragma once
#include <iostream>
#include <string>
#include <map>
#include <sstream>
#include <iomanip>
#include <algorithm>

/** Custom comparator: ignore case */
struct CaseInsensitiveCompare {
	bool operator()(const std::string& a, const std::string& b) const {
		return std::lexicographical_compare(
			a.begin(), a.end(), b.begin(), b.end(),
			[](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); }
		);
	}
};

/**@link https://www.w3school.com.cn/tags/html_ref_colornames.asp */
static const std::map<std::string, std::string, CaseInsensitiveCompare> PredefinedColors = {
		{"AliceBlue", "#F0F8FF"}, {"AntiqueWhite", "#FAEBD7"}, {"Aqua", "#00FFFF"},
		{"Aquamarine", "#7FFFD4"}, {"Azure", "#F0FFFF"}, {"Beige", "#F5F5DC"},
		{"Bisque", "#FFE4C4"}, {"Black", "#000000"}, {"BlanchedAlmond", "#FFEBCD"},
		{"Blue", "#0000FF"}, {"BlueViolet", "#8A2BE2"}, {"Brown", "#A52A2A"},
		{"BurlyWood", "#DEB887"}, {"CadetBlue", "#5F9EA0"}, {"Chartreuse", "#7FFF00"},
		{"Chocolate", "#D2691E"}, {"Coral", "#FF7F50"}, {"CornflowerBlue", "#6495ED"},
		{"Cornsilk", "#FFF8DC"}, {"Crimson", "#DC143C"}, {"Cyan", "#00FFFF"},
		{"DarkBlue", "#00008B"}, {"DarkCyan", "#008B8B"}, {"DarkGoldenRod", "#B8860B"},
		{"DarkGray", "#A9A9A9"}, {"DarkGreen", "#006400"}, {"DarkKhaki", "#BDB76B"},
		{"DarkMagenta", "#8B008B"}, {"DarkOliveGreen", "#556B2F"}, {"Darkorange", "#FF8C00"},
		{"DarkOrchid", "#9932CC"}, {"DarkRed", "#8B0000"}, {"DarkSalmon", "#E9967A"},
		{"DarkSeaGreen", "#8FBC8F"}, {"DarkSlateBlue", "#483D8B"}, {"DarkSlateGray", "#2F4F4F"},
		{"DarkTurquoise", "#00CED1"}, {"DarkViolet", "#9400D3"}, {"DeepPink", "#FF1493"},
		{"DeepSkyBlue", "#00BFFF"}, {"DimGray", "#696969"}, {"DodgerBlue", "#1E90FF"},
		{"Feldspar", "#D19275"}, {"FireBrick", "#B22222"}, {"FloralWhite", "#FFFAF0"},
		{"ForestGreen", "#228B22"}, {"Fuchsia", "#FF00FF"}, {"Gainsboro", "#DCDCDC"},
		{"GhostWhite", "#F8F8FF"}, {"Gold", "#FFD700"}, {"GoldenRod", "#DAA520"},
		{"Gray", "#808080"}, {"Green", "#008000"}, {"GreenYellow", "#ADFF2F"},
		{"HoneyDew", "#F0FFF0"}, {"HotPink", "#FF69B4"}, {"IndianRed", "#CD5C5C"},
		{"Indigo", "#4B0082"}, {"Ivory", "#FFFFF0"}, {"Khaki", "#F0E68C"},
		{"Lavender", "#E6E6FA"}, {"LavenderBlush", "#FFF0F5"}, {"LawnGreen", "#7CFC00"},
		{"LemonChiffon", "#FFFACD"}, {"LightBlue", "#ADD8E6"}, {"LightCoral", "#F08080"},
		{"LightCyan", "#E0FFFF"}, {"LightGoldenRodYellow", "#FAFAD2"}, {"LightGrey", "#D3D3D3"},
		{"LightGreen", "#90EE90"}, {"LightPink", "#FFB6C1"}, {"LightSalmon", "#FFA07A"},
		{"LightSeaGreen", "#20B2AA"}, {"LightSkyBlue", "#87CEFA"}, {"LightSlateBlue", "#8470FF"},
		{"LightSlateGray", "#778899"}, {"LightSteelBlue", "#B0C4DE"}, {"LightYellow", "#FFFFE0"},
		{"Lime", "#00FF00"}, {"LimeGreen", "#32CD32"}, {"Linen", "#FAF0E6"},
		{"Magenta", "#FF00FF"}, {"Maroon", "#800000"}, {"MediumAquaMarine", "#66CDAA"},
		{"MediumBlue", "#0000CD"}, {"MediumOrchid", "#BA55D3"}, {"MediumPurple", "#9370D8"},
		{"MediumSeaGreen", "#3CB371"}, {"MediumSlateBlue", "#7B68EE"}, {"MediumSpringGreen", "#00FA9A"},
		{"MediumTurquoise", "#48D1CC"}, {"MediumVioletRed", "#C71585"}, {"MidnightBlue", "#191970"},
		{"MintCream", "#F5FFFA"}, {"MistyRose", "#FFE4E1"}, {"Moccasin", "#FFE4B5"},
		{"NavajoWhite", "#FFDEAD"}, {"Navy", "#000080"}, {"OldLace", "#FDF5E6"},
		{"Olive", "#808000"}, {"OliveDrab", "#6B8E23"}, {"Orange", "#FFA500"},
		{"OrangeRed", "#FF4500"}, {"Orchid", "#DA70D6"}, {"PaleGoldenRod", "#EEE8AA"},
		{"PaleGreen", "#98FB98"}, {"PaleTurquoise", "#AFEEEE"}, {"PaleVioletRed", "#D87093"},
		{"PapayaWhip", "#FFEFD5"}, {"PeachPuff", "#FFDAB9"}, {"Peru", "#CD853F"},
		{"Pink", "#FFC0CB"}, {"Plum", "#DDA0DD"}, {"PowderBlue", "#B0E0E6"},
		{"Purple", "#800080"}, {"Red", "#FF0000"}, {"RosyBrown", "#BC8F8F"},
		{"RoyalBlue", "#4169E1"}, {"SaddleBrown", "#8B4513"}, {"Salmon", "#FA8072"},
		{"SandyBrown", "#F4A460"}, {"SeaGreen", "#2E8B57"}, {"SeaShell", "#FFF5EE"},
		{"Sienna", "#A0522D"}, {"Silver", "#C0C0C0"}, {"SkyBlue", "#87CEEB"},
		{"SlateBlue", "#6A5ACD"}, {"SlateGray", "#708090"}, {"Snow", "#FFFAFA"},
		{"SpringGreen", "#00FF7F"}, {"SteelBlue", "#4682B4"}, {"Tan", "#D2B48C"},
		{"Teal", "#008080"}, {"Thistle", "#D8BFD8"}, {"Tomato", "#FF6347"},
		{"Turquoise", "#40E0D0"}, {"Violet", "#EE82EE"}, {"VioletRed", "#D02090"},
		{"Wheat", "#F5DEB3"}, {"White", "#FFFFFF"}, {"WhiteSmoke", "#F5F5F5"},
		{"Yellow", "#FFFF00"}, {"YellowGreen", "#9ACD32"}
};

class CColor {
public:
	CColor() : r(-1), g(-1), b(-1) {}

	CColor(int red, int green, int blue, int alpha = 255) : r(red), g(green), b(blue), a(alpha) {}

	CColor(const char* colorStr) : CColor(std::string(colorStr)) {}

	CColor(const std::string& colorStr) {
		r = g = b = 0;
		if ("transparent" == colorStr) {
			a = 0;
			return;
		}
		if (colorStr[0] == '#' && (colorStr.length() == 7 || colorStr.length() == 9))
			hexToColor(colorStr);
		else {
			auto it = PredefinedColors.find(colorStr);
			if (it != PredefinedColors.end())
				hexToColor(it->second);
			else
				std::cerr << "error: unknown color name!" << std::endl;
		}
	}

	// allow `CColor c = "red";`
	CColor& operator=(const char* colorString) {
		*this = CColor(colorString);
		return *this;
	}
	CColor& operator=(const std::string& colorString) {
		*this = CColor(colorString);
		return *this;
	}

	bool operator==(const CColor& other) const {
		return (r == other.r) && (g == other.g) && (b == other.b) && (a == other.a);
	}

	bool operator==(const char* colorString) const {
		CColor other(colorString);
		return *this == other;
	}

	// allow `std::cout << color;`
	friend std::ostream& operator<<(std::ostream& os, const CColor& color) {
		os << color.toString();
		return os;
	}

	bool isValid() const { return r >= 0 && g >= 0 && b >= 0; }

	void setAlpha(float alphaF) { a = alphaF * 255; m_hasSetAlphaChannel = true; }

	void eraseAlphaChannel() { a = 255; m_hasSetAlphaChannel = false; }

	bool hasAlphaChannel() const {
		if (m_hasSetAlphaChannel) return true;
		return a != 255;
	}

	float redF() const { return r / 255.0f; }
	float greenF() const { return g / 255.0f; }
	float blueF() const { return b / 255.0f; }
	float alphaF() const { return a / 255.0f; }

	static CColor fromRgbF(float redF, float greenF, float blueF, float alphaF = 1.0) {
		return CColor(redF * 255, greenF * 255, blueF * 255, alphaF * 255);
	}

	static CColor fromRgb(int red, int green, int blue, int alpha = 255) {
		return CColor(red, green, blue, alpha);
	}

	CColor lighter(int factor = 150) const {// same as QColor::lighter
		return CColor(adjustBrightness(factor));
	}

	CColor darker(int factor = 200) const {// same as QColor::darker
		return CColor(adjustBrightness(10000 / factor)); // reverse calculation
	}

	std::string toString() const {
		std::stringstream ss;
		ss << "#" << std::hex << std::uppercase << std::setfill('0')
		   << std::setw(2) << r
		   << std::setw(2) << g
		   << std::setw(2) << b
		   << std::setw(2) << a;
		return ss.str();
	}

private:
	void hexToColor(const std::string& hex) {// format "#RRGGBB" or "#RRGGBBAA"
		r = hexToInt(hex.substr(1, 2));
		g = hexToInt(hex.substr(3, 2));
		b = hexToInt(hex.substr(5, 2));
		if (hex.length() == 9) {
			a = hexToInt(hex.substr(7, 2));
			m_hasSetAlphaChannel = true;
		}
	}

	static int hexToInt(const std::string& hex) {
		int value;
		std::istringstream(hex) >> std::hex >> value;
		return value;
	}

	std::string adjustBrightness(int factor) const {
		int newR = std::clamp(r * factor / 100, 0, 255);
		int newG = std::clamp(g * factor / 100, 0, 255);
		int newB = std::clamp(b * factor / 100, 0, 255);

		std::ostringstream oss;
		oss << "#" << std::hex << std::setfill('0') << std::setw(2) << newR
			<< std::setw(2) << newG << std::setw(2) << newB;
		return oss.str();
	}

	int r, g, b, a = 255;

	bool m_hasSetAlphaChannel = false;
};